
#include <stdlib.h>
#include <string.h>
#include "dlist.h"


/** dlist_init */

void dlist_init(DList *list, void (*destroy)(void *data))
{
    /**
     * Initialize the list.
     */
    list->size    = 0;
    list->destroy = destroy;
    list->head    = NULL;
    list->tail    = NULL;
    return;
}

void dlist_destroy(DList *list)
{
    void *data;

    /** Remove each element. */
    while (dlist_size(list) > 0) {
        if (dlist_remove(list, dlist_tail(list), (void **)&data) == 0 && list->destroy != NULL) {
            list->destroy(data);
        }
    }

    memset(list, 0, sizeof(DList));
}

int dlist_ins_next(DList *list, DListElmt *element, const void *data)
{
    DListElmt *new_element;

    /** Do not allow a NULL element unless the list is empty */
    if (element == NULL && dlist_size(list) != 0) {
        return -1;
    }

    /** Allocate storage for the element. */
    if ((new_element = (DListElmt *)malloc(sizeof(DListElmt
                                                 ))) == NULL) {
        return -1;
    }

    new_element->data = (void *)data;

    if (dlist_size(list) == 0) {
        /** Handle insertion when the list is empty. */
        list->head       = new_element;
        list->head->prev = NULL;
        list->tail       = new_element;
        list->tail->next = NULL;
    } else {
        new_element->next = element->next;
        new_element->prev = element;

        if (element->next == NULL) {
            list->tail = new_element;
        } else {
            element->next->prev = new_element;
        }

        element->next     = new_element;
    }

    list->size++;
    return 0;
}

int dlist_ins_prev(DList *list, DListElmt *element, const void *data)
{
    DListElmt *new_element;

    /** Do not allow a NULL element unless the thist is empty. */
    if (element == NULL && dlist_size(list) != 0) {
        return -1;
    }

    /** Allocate storage to be managed by the abstract datatype. */
    if ((new_element = (DListElmt *)malloc(sizeof(DListElmt))) == 0) {
        return -1;
    }

    new_element->data = (void *)data;

    if (dlist_size(list) == 0) {
        list->head        = new_element;
        list->tail        = new_element;
        new_element->next = NULL;
        new_element->prev = NULL;
    } else {
        new_element->prev = element->prev;
        new_element->next = element;

        if (element->prev == NULL) {
            list->head = new_element;
        } else {
            element->prev->next = new_element;
        }

        element->prev = new_element;
    }

    list->size++;
    return 0;
}

int dlist_remove(DList *list, DListElmt *element, void **data)
{
    if (element == NULL || dlist_size(list) == 0) {
        return -1;
    }

    *data = element->data;

    if (element == list->head) {
        list->head = element->next;

        if (list->head == NULL) {
            list->tail = NULL;
        } else {
            element->next->prev = NULL;
        }
    } else {
        element->prev->next = element->next;

        if (element->next == NULL) {
            list->tail = element->prev;
        } else {
            element->next->prev = element->prev;
        }
    }

    free(element);
    list->size--;
    return 0;
}
